package util;

import java.io.Serializable;


public class Vector3f extends Tuple3f implements Serializable
{
    /**
	 * 
	 */
	private static final long serialVersionUID = -6563053391649548760L;

	/**
     * Constructs and initializes a Vector3f to (0,0,0).
     */

    public Vector3f()
    {
    }


    /**
     * Constructs and initializes a Vector3f from the specified xyz coordinates.
     * @param x
     * @param y
     * @param z
     */

    public Vector3f(float x, float y, float z)
    {
        super(x, y, z);
    }


    /**
     * Constructs and initializes a Vector3f from the specified Vector3f
     * @param v
     */

    public Vector3f(Vector3f v)
    {
        super(v);
    }


    /**
     * Constructs and initializes a Vector3f from the specified Tuple3f.
     * @param t
     */

    public Vector3f(Tuple3f t)
    {
        super(t);
    }


    /**
     * Constructs and initializes a Vector3f from the array of length 3.
     * @param v
     */

    public Vector3f(float v[])
    {
        super(v);
    }
    
    /**
     * Constructs and initializes a Vector3f from the first three components of a Vector4f
     * @param v
     */
    public Vector3f(Vector4f v){
    	super(v.x,v.y,v.z);
    }
    
    /**
     * Adds v to this vector
     * @param v the vector to add to this vector
     * @return w = this + v
     */
    public Vector3f add(Vector3f v){
    	return new Vector3f(this.x + v.x, this.y + v.y, this.z + v.z);
    }
    
    /**
     * Subtracts v from this vector
     * @param v the vector to subtract from this vector
     * @return w = this - v
     */
    public Vector3f subtract(Vector3f v){
    	return new Vector3f(this.x - v.x, this.y - v.y, this.z - v.z);
    }
    
    /**
     * Scales this vector with a scalar r
     * @param r the scalar to scale this vector by
     * @return w = r * v
     */
    public Vector3f scale(float r){
    	return new Vector3f(this.x * r, this.y * r, this.z * r);
    }
    
    /**
     * Returns the dot product (scalar product, inner product) of this vector and v (this . v)
     * @param v the vector to take the cross product with
     * @return w = this . v
     */
    public float dot(Vector3f v){
    	return this.x * v.x + this.y * v.y +  this.z * v.z;
    }
    
    /**
     * Returns the cross product of this vector and v (this x v)
     * @param v the vector to take the cross product with
     * @return w = this x v
     */
    public Vector3f cross(Vector3f v){
    	float x = this.y * v.z - this.z * v.y;
    	float y = this.z * v.x - this.x * v.z;
    	float z = this.x * v.y - this.y * v.x;
    	return new Vector3f(x,y,z);
    }
    
    /**
     * Returns the length (modulus) of this vector
     * @return |this|
     */
    public float length(){
    	return (float)Math.sqrt(x*x + y*y + z*z);
    }
    
    /**
     * Returns the modulus (length) of this vector
     * @return |this|
     */
    public float modulus(){
    	return length();
    }
    
    /**
     * Returns the normalized vector of this
     * @return this/|this|
     */
    public Vector3f normalize(){
    	if(x == 0 && y == 0 && z == 0)
    		return this;
    	return this.scale(1/this.length());
    }
    
//    /**
//     * Indicates whether this vector is perpendicular to the other one.
//     * @return true if the vectors are perpendicular
//     */
//    public boolean isPerpendicularTo(Vector3f v){
//    	return (this.dot(v) <= 0.00000001f && this.dot(v) >= 0f);
//    }
    
    public boolean equals(Object other){
    	if(!(other instanceof Vector3f))
    		return false;
    	Vector3f o = (Vector3f)other;
    	return x==o.x && y==o.y && z==o.z;
    }
    
    /**
     * Component-wise division
     * @param v
     * @return
     */
    public Vector3f divide(Vector3f v){
    	float i = x / v.x,
    		  j = y / v.y,
    		  k = z / v.z;
    	return new Vector3f(i,j,k);
    }
    
    public float get(Axis axis){
    	switch(axis){
    	case X: return x;
    	case Y: return y;
    	case Z: return z;
    	default: return Float.NaN;
    	}
    }
    
    public float get(int axis){
    	switch(axis){
    	case 0: return x;
    	case 1: return y;
    	case 2: return z;
    	default: return Float.NaN;
    	}
    }
    
    public void set(Axis axis, float value){
    	switch(axis){
    	case X: this.x = value; break;
    	case Y: this.y = value; break;
    	case Z: this.z = value; break;
    	default: break;
    	}
    }
    
    /**
     * Returns the component with the largest absolute value
     */
    public Axis getMaxComponent(){
    	if(Math.abs(x) > Math.abs(y)){
    		if(Math.abs(z) > Math.abs(x))
    			return Axis.Z;
    		return Axis.X;
    	}
    	else{//x <= y
    		if(Math.abs(z) > Math.abs(y))
    			return Axis.Z;
    		return Axis.Y;
    	}
    }
}
